package test;

import static org.junit.Assert.*;
import movement.Location;

import org.junit.*;

import unit.*;

import dispatch.*;
import emergency.*;

public class testUnit {
	DispatchCenter center;
	Garage garage;
	Unit u1, u2, u3, u4, u5, u6;
	Emergency em1, em2;

	@Before
	public void setUp() throws Exception {
		center = new DispatchCenter("DispatchCenter",
				new Location(10, 10, "DC"), new Location(50, 50, "Garage"));
		garage = center.getGarage();
		u1 = new PoliceCar("Policecar-1", 100, garage);
		u2 = new Ambulance("Ambulance-1", 140, garage);
		u3 = new PoliceCar("Policecar-2", 110, garage);
		u4 = new FireTruck("Firetruck-1", 90, garage, Fire.Size.FACILITY);
		u5 = new FireTruck("Firetruck-2", 100, garage, Fire.Size.FACILITY);
		u6 = new PoliceCar("Policecar-3", 110, garage);

		em1 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, false, false);
		em2 = new TrafficAccident(new Location(200, 200, "Boem"),
				Severity.SERIOUS, center, 1, 1);
	}

	@Test
	public void testAssignEmergency() {
		u1.assignEmergency(em2);
		assertEquals(u1.getEmergency(), em2);
		assertTrue(em2.getUnits().contains(u1));
	}

	@Test
	public void testAssignInvalidEmergency() {
		u1.brokeDown(); // broken unit can't be assigned
		assertEquals(null, u1.getEmergency());

		u3.assignEmergency(em2);
		u3.assignEmergency(em1);// assigned unit can't be assigned
		assertEquals(u3.getEmergency(), em2);
	}

	@Test
	public void testBrokeDown1() throws IllegalAccessException {
		// unit without emergency
		assertFalse(u1.isBroken());
		assertFalse(u1.isAssigned());
		u1.brokeDown();
		assertTrue(u1.isBroken());

		// unit with emergency, new unit available
		em1.assignAllUnits(garage);
		assertEquals(em1, u3.getEmergency());
		u3.brokeDown();
		assertTrue(u3.isBroken());
		assertEquals(u3.getTargetLocation().getX(), u3.getCurrentLocation()
				.getX());
		assertEquals(u3.getTargetLocation().getY(), u3.getCurrentLocation()
				.getY());
		assertFalse(em1.getUnits().contains(u3));
	}

	@Test
	public void testBrokeDown2() throws IllegalAccessException {
		// unit with emergency, no new unit available
		em1 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, true, true);
		em1.assignAllUnits(garage);

		assertEquals(em1, u1.getEmergency());
		assertEquals(em1, u3.getEmergency());
		assertEquals(em1, u6.getEmergency());

		u3.brokeDown();

		// no new units available => units stays assigned
		assertTrue(u3.isBroken());
		assertEquals(em1, u3.getEmergency());
		assertTrue(em1.getUnits().contains(u3));
	}

	@Test(expected = IllegalArgumentException.class)
	public void testBrokeDownAlreadyBrokenDownEmergency() {
		u1.assignEmergency(em2);
		u1.brokeDown();
		u1.brokeDown();
	}

	@Test(expected = IllegalArgumentException.class)
	public void testBrokeDownAlreadyBrokenDownNoEmergency() {
		u1.brokeDown();
		u1.brokeDown();
	}

	@Test
	public void testCanBeAssigned1() {
		assertTrue(u1.canBeAssigned());
		assertTrue(u3.canBeAssigned());
		u1.brokeDown();
		u3.assignEmergency(em2);
		assertFalse(u1.canBeAssigned());
		assertFalse(u3.canBeAssigned());
	}

	@Test(expected = IllegalArgumentException.class)
	public void testCanBeAssigned2() {
		// this fails because u1 and u3 doesn't have an emergency yet
		assertTrue(u1.canBeAssigned(em1));
		assertTrue(u3.canBeAssigned(em2));
	}

	@Test
	public void testConstructor() {
		assertEquals("Policecar-1", u1.getName());
		assertEquals(null, u1.getEmergency());
		assertEquals(false, u1.getBroken());
		assertEquals(garage, u1.getHomeLocation());
		assertEquals(garage.getLocation().getX(), u1.getCurrentLocation()
				.getX());
		assertEquals(garage.getLocation().getY(), u1.getCurrentLocation()
				.getY());
		assertEquals(null, u1.getTargetLocation());
		assertEquals(100, u1.getSpeed());
		assertTrue(garage.getUnitsClone().contains(u1));
	}

	@SuppressWarnings("unused")
	@Test(expected = IllegalArgumentException.class)
	public void testConstructorNameOfUnitAlreadyInGarage() {
		PoliceCar pc1 = new PoliceCar("Policecar-1", 120, garage);
	}

	@SuppressWarnings("unused")
	@Test(expected = IllegalArgumentException.class)
	public void testConstructorNullGarage() {
		PoliceCar pc1 = new PoliceCar("Policecar-11", 100, null);
	}

	@Test
	public void testEmergencyCanBeFinished() {
		assertFalse(u1.emergencyCanBeFinished());

		u1.assignEmergency(em2);
		assertFalse(u1.emergencyCanBeFinished());

		u1.setCurrentLocation(em2.getLocation());
		assertTrue(u1.emergencyCanBeFinished());
	}

	@Test(expected = IllegalArgumentException.class)
	public void testEndEmergencyNoEmergency() {
		u1.endEmergency();
	}

	@Test(expected = IllegalArgumentException.class)
	public void testEndEmergencyNotAtEmergency() {
		u1.assignEmergency(em1);
		u1.endEmergency();
	}

	@Test
	public void testEndEmergencyUnit() throws IllegalAccessException {
		em1 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, true, true);

		em1.assignAllUnits(garage);
		u1.setCurrentLocation(em1.getLocation());
		u3.setCurrentLocation(em1.getLocation());
		u6.setCurrentLocation(em1.getLocation());

		// simulate all units on emergency
		assertTrue(u1.getCurrentLocation().equals(em1.getLocation()));
		assertTrue(u3.getCurrentLocation().equals(em1.getLocation()));
		assertTrue(u6.getCurrentLocation().equals(em1.getLocation()));
		u3.endEmergency();
		u1.endEmergency();
		u6.endEmergency();

		// emergency completed
		assertEquals("statecompleted", em1.getStateName());
		// units can be assigned again
		assertTrue(u1.canBeAssigned());
		assertTrue(u3.canBeAssigned());
		assertTrue(u6.canBeAssigned());
	}

	@SuppressWarnings("unused")
	@Test(expected = IllegalArgumentException.class)
	public void testEndEmergencyUnitNeedsToWaitForCollegues()
			throws IllegalAccessException {
		Unit u4 = new PoliceCar("Policecar-3", 110, garage);
		Unit u5 = new PoliceCar("Policecar-4", 110, garage);
		em1 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, true, true);
		em1.assignAllUnits(garage);

		u3.setCurrentLocation(em1.getLocation());

		// only one unit is at emergency (u3)
		u3.endEmergency();
	}

	@Test
	public void testEquals() {
		assertFalse(u1.equals(u3));
		assertTrue(u1.equals(u1));
	}

	@Test
	public void testGettersAndSetters() {
		Location l1 = new Location(20, 25, "testje");
		Location l2 = new Location(25, 25, "testje2");
		u1.setCurrentLocation(l1);
		u1.setHomeLocation(garage);
		u1.setName("Jakke");
		u1.setSpeed(-20);
		u1.setTargetLocation(l2);
		assertTrue(l1.equals(u1.getCurrentLocation()));
		assertTrue(garage.equals(u1.getHomeLocation()));
		assertEquals("Jakke", u1.getName());
		assertEquals(10, u1.getSpeed());
		assertTrue(l2.equals(u1.getTargetLocation()));
	}

	@Test
	public void testIsAssigned() {
		assertFalse(u1.isAssigned());
		u1.assignEmergency(em2);
		assertTrue(u1.isAssigned());
	}

	@Test(expected = IllegalArgumentException.class)
	public void testRepair() {
		// u1 isn't broken down
		u1.repair();
	}

	@Test
	public void testRepairNoEmergency() throws IllegalAccessException {
		em1.assignAllUnits(garage);

		assertEquals(em1, u3.getEmergency());

		u3.brokeDown();

		// new units available => units is un-assigned
		assertTrue(u3.isBroken());
		assertEquals(null, u3.getEmergency());
		assertFalse(em1.getUnits().contains(u3));

		u3.repair();

		assertFalse(u3.isBroken());
		assertEquals(null, u3.getEmergency());
		assertFalse(em1.getUnits().contains(u3));
	}

	@Test
	public void testRepairStillAssignedOnEmergency()
			throws IllegalAccessException {
		// unit is still assigned on emergency
		em1 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, true, true);
		em1.assignAllUnits(garage);

		assertEquals(em1, u1.getEmergency());
		assertEquals(em1, u3.getEmergency());
		assertEquals(em1, u6.getEmergency());

		u3.brokeDown();

		// no new units available => units stays assigned
		assertTrue(u3.isBroken());
		assertEquals(em1, u3.getEmergency());
		assertTrue(em1.getUnits().contains(u3));

		u3.repair();

		assertEquals(u3.getTargetLocation().getX(), em1.getLocation().getX());
		assertEquals(u3.getTargetLocation().getY(), em1.getLocation().getY());
	}

	@Test
	public void testRevokeUnit() throws IllegalAccessException {
		Garage homeloc = u1.getHomeLocation();
		u1.assignEmergency(em2);

		assertEquals(u1.getEmergency(), em2);
		assertTrue(em2.getUnits().contains(u1));

		u1.revokeUnit();

		// unit revoked
		assertEquals(u1.getEmergency(), null);
		assertFalse(em2.getUnits().contains(u1));
		assertTrue(u1.getCurrentLocation().equals(homeloc.getLocation()));

	}

	@Test(expected = IllegalArgumentException.class)
	public void testRevokeUnitNoEmergency() throws IllegalAccessException {
		// can't revoke if unit doesn't has an emergency
		u1.revokeUnit();
	}

	@Test(expected = IllegalAccessException.class)
	public void testRevokeUnitOnEmergency() throws IllegalAccessException {
		u1.assignEmergency(em2);

		assertEquals(u1.getEmergency(), em2);
		assertTrue(em2.getUnits().contains(u1));

		// u1 is at emergency
		u1.setCurrentLocation(em2.getLocation());
		assertEquals(em2.getLocation().getX(), u1.getCurrentLocation().getX());
		assertEquals(em2.getLocation().getY(), u1.getCurrentLocation().getY());

		// can't revoke if unit is on the emergency
		u1.revokeUnit();
	}

	@Test
	public void testUnitIsInGarage() {
		u1.setCurrentLocation(new Location(921, 123, "jep"));
		//assertFalse(u1.unitIsInGarage());
		assertFalse(u1.getCurrentLocation().equals(u1.getHomeLocation().getLocation()));
		u1.setCurrentLocation(garage.getLocation());
		//assertTrue(u1.unitIsInGarage());
		assertTrue(u1.getCurrentLocation().equals(u1.getHomeLocation().getLocation()));
	}

	@Test
	public void testUnitIsOnEmergency(){
		u1.assignEmergency(em2);
		assertFalse(u1.isAtEmergency());
		u1.setCurrentLocation(em2.getLocation());
		assertTrue(u1.isAtEmergency());
	}

	@Test (expected=IllegalArgumentException.class)
	public void testUnitIsOnEmergencyNoEmergency(){
		u1.isAtEmergency();
	}

	@Test
	public void testUnitOfSameTypeAvailable1() throws IllegalAccessException {
		em2 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, true, true);
		// all 3 policecars are assigned!
		em2.assignAllUnits(garage);
		assertFalse(u1.unitOfSameTypeAvailable());

	}
	
	@Test
	public void testUnitOfSameTypeAvailable2() throws IllegalAccessException {
		em2 = new Robbery(new Location(100, 100, "Robbery"), Severity.BENIGN,
				center, false, false);
		// only 1 policecar (u3) is assigned!
		em2.assignAllUnits(garage);
		assertTrue(u3.unitOfSameTypeAvailable());
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void testUnitOfSameTypeAvailableNoEmergency() {
		// u1 hasn't got an emergency yet
		u1.unitOfSameTypeAvailable();
	}
}
